const FULFILLED = "fulfilled"; // 成功
const REJECTED = "rejected"; // 失败
const PENDING = "pending";

/**
 * 手写Promise
 */
class MyPromiseA {
  #state = PENDING;
  #result = undefined;
  #handlers = [];

  /**
   * 构造函数
   * @param {Function} executor 构造器函数
   * @returns void
   */
  constructor(executor) {
    const resolve = data => {
      this.#changeState(FULFILLED, data);
    };
    const reject = reason => {
      this.#changeState(REJECTED, reason);
    };
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  /**
   * 改变状态的函数
   * @param {string} state 状态
   * @param {any} result 结果
   * @returns void
   */
  #changeState(state, result) {
    // 成功回调，改变状态，一旦状态改变就不可更改
    if (this.#state !== PENDING) return;
    this.#state = state;
    this.#result = result;
    this.#run();
  }

  /**
   * 判断是否有then方法
   * @param {any} value 判断参数
   * @returns 布尔值
   */
  #isPromiseLike(value) {
    if (value !== null && (typeof value === "object" || typeof value == "function")) {
      return typeof value.then === "function";
    }
    return false;
  }

  /**
   * 模仿环境的微任务
   * @param {Function} func 回调函数
   * @returns void
   */
  #runMicroTask(func) {
    if (typeof process === "object" && typeof process.nextTick === "function") {
      process.nextTick(func);
    } else if (typeof MutationObserver === "function") {
      const ob = new MutationObserver(func);
      const textNode = document.createTextNode("1");
      ob.observe(textNode, { characterData: true });
      textNode.data = "2";
    } else {
      setTimeout(func, 0);
    }
  }

  /**
   * 遵循DRY原则(Don't Repeat Yourself)
   * @param {Function} callback 回调函数
   * @param {Function} resolve 回调函数
   * @param {Function} reject 回调函数
   * @returns void
   */
  #runOne(callback, resolve, reject) {
    this.#runMicroTask(() => {
      if (typeof callback !== "function") {
        const settled = this.#state === FULFILLED ? resolve : reject;
        settled(this.#result);
        return;
      }
      try {
        const data = callback(this.#result);
        if (this.#isPromiseLike(data)) {
          // 穿透性
          data.then(resolve, reject);
        } else {
          resolve(data);
        }
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 状态变化执行函数
   * @returns void
   */
  #run() {
    if (this.#state === PENDING) return;
    while (this.#handlers.length) {
      const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift();
      if (this.#state === FULFILLED) {
        this.#runOne(onFulfilled, resolve, reject);
      } else {
        this.#runOne(onRejected, resolve, reject);
      }
    }
  }

  /**
   * 最重要的then方法
   * @param {Function} onFulfilled 回调函数
   * @param {Function} onRejected 回调函数
   * @returns 新的MyPromise
   */
  then(onFulfilled, onRejected) {
    return new MyPromiseA((resolve, reject) => {
      this.#handlers.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      });
      this.#run();
    });
  }

  /**
   * 其他方法ES6里面的-catch
   * @param {Function} onRejected 回调函数
   * @returns 新的MyPromise
   */
  catch(onRejected) {
    return this.then(undefined, onRejected);
  }

  /**
   * 其他方法ES6里面的-finally
   * @param {Function} onFinally 回调函数
   * @returns 新的MyPromise
   */
  finally(onFinally) {
    return this.then(
      data => {
        onFinally();
        return data;
      },
      reason => {
        onFinally();
        throw reason;
      }
    );
  }

  /**
   * 静态方法resolve
   * @param {any} value 成功结果
   * @returns 新的MyPromise
   */
  static resolve(value) {
    if (value instanceof MyPromiseA) return value;
    let _resolve, _reject;
    const p = new MyPromiseA((resolve, reject) => {
      _resolve = resolve;
      _reject = reject;
    });
    if (p.#isPromiseLike(value)) {
      value.then(_resolve, _reject);
    } else {
      _resolve(value);
    }
    return p;
  }

  /**
   * 静态方法reject
   * @param {any} reason 失败结果
   * @returns 新的MyPromise
   */
  static reject(reason) {
    return new MyPromiseA((resolve, reject) => {
      reject(reason);
    });
  }

  /**
   * 判断是可迭代对象
   * @param {any} value 参数
   * @returns 布尔值
   */
  #isIterable(value) {
    if (typeof value === "string") {
      return true;
    }
    if (value !== null && (typeof value == "object" || typeof value === "function")) {
      return typeof value[Symbol.iterator] === "function";
    }
    return false;
  }

  /**
   * 静态方法all
   * @param {Iterable} proms 可迭代对象
   * @returns 新的MyPromise
   */
  static all(proms) {
    let _resolve, _reject;
    let count = 0,
      finishNum = 0;
    const result = [];

    const p = new MyPromiseA((resolve, reject) => {
      _resolve = resolve;
      _reject = reject;
    });

    if (p.#isIterable(proms)) {
      for (const prom of proms) {
        const index = count; // 获取下标
        count++;
        MyPromiseA.resolve(prom).then(data => {
          result[index] = data; // 将成功的值加入数组

          finishNum++;
          // 判断是否全部完成
          if (finishNum === count) {
            _resolve(result);
          }
        }, _reject);
      }
      if (count === 0) {
        // 表示空数组
        _resolve([]);
      }
    } else {
      // 如果iterator不是可迭代对象，则reject
      _reject(`TypeError: ${typeof proms} ${proms} is not iterable (cannot read property Symbol(Symbol.iterator))`);
    }
    return p;
  }

  /**
   * 静态方法race
   * @param {Iterable} proms 可迭代对象
   * @returns 新的MyPromise
   */
  static race(proms) {
    let _resolve, _reject;

    const p = new MyPromiseA((resolve, reject) => {
      _resolve = resolve;
      _reject = reject;
    });

    // 谁先结束状态就发生变化
    if (p.#isIterable(proms)) {
      for (const prom of proms) {
        MyPromiseA.resolve(prom).then(_resolve, _reject);
      }
    } else {
      // 如果iterator不是可迭代对象，则reject
      _reject(`TypeError: ${typeof proms} ${proms} is not iterable (cannot read property Symbol(Symbol.iterator))`);
    }

    return p;
  }

  /**
   * 静态方法allSettled
   * @param {*} proms 可迭代对象
   * @returns 新的MyPromise
   */
  static allSettled(proms) {
    let _resolve, _reject;
    let count = 0,
      total = 0;
    const result = [];

    const p = new MyPromiseA((resolve, reject) => {
      _resolve = resolve;
      _reject = reject;
    });

    if (p.#isIterable(proms)) {
      for (const prom of proms) {
        const index = count;
        count++;
        MyPromiseA.resolve(prom)
          .then(value => {
            result[index] = {
              status: "fulfilled",
              value
            };
          })
          .catch(reason => {
            result[index] = {
              status: "rejected",
              reason
            };
          })
          .finally(() => {
            total++;
            if (total === count) {
              // 全部状态都结束
              _resolve(result);
            }
          });
      }

      // 如果可迭代对象是空的则返回空数组
      if (count === 0) {
        _resolve([]);
      }
    } else {
      // 如果iterator不是可迭代对象，则reject
      _reject(`TypeError: ${typeof proms} ${proms} is not iterable (cannot read property Symbol(Symbol.iterator))`);
    }

    return p;
  }
}

// 测试

const promise1 = new Promise((resolve, reject) => {
  setTimeout(resolve, 500, "promise1");
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(reject, 100, "promise2");
});

const p = MyPromiseA.allSettled([promise1, promise2])
  .then(data => {
    console.log("data ", data);
  })
  .catch(err => {
    console.log("err ", err);
  });

/* setTimeout(() => {
  console.log(1);
}, 0); */

/* const p = new MyPromiseA((resolve, reject) => {
  reject(1);
});

MyPromiseA.reject(22).then(undefined, err => {
  console.log("err ", err);
}); */

/* MyPromiseA.resolve(p).then(data => {
  console.log("data ", data);
}); */
// console.log(MyPromiseA.resolve(p) === p);

/* const p = new MyPromiseA((resolve, reject) => {
  reject(2);
})
  .finally(data => {
    console.log("data ", data);
  })
  .then(data => {
    console.log("finally ", data);
  })
  .catch(err => {
    console.log("err ", err);
  }); */

// console.log(3);

/* p.then(
  data => {
    return new Promise((resolve, reject) => {
      console.log("promise1 ", data);
      setTimeout(() => {
        resolve(456);
      }, 1000);
    });
  },
  reason => {
    throw reason;
  }
).then(
  data => {
    console.log("data ", data);
  },
  reason => {
    console.log("error ", reason);
  }
); */

/* p.then(
  data => {
    console.log("promise 完成2 " + data);
  },
  reason => {
    console.log("promise 失败2 " + reason);
  }
); */

/* console.log(p); */
